#include "ExpAST.h"
#include "Globe.h"
#include <iostream>
#include "parser.h"
#include "Globe.h"
#pragma warning(disable:4267)
using std::cout;
using std::endl;

unique_ptr<BaseExpAST> parser::parseAssignExp()
{
    unique_ptr<BaseExpAST>ret = this->parseTernaryExp();
    unique_ptr<AssignAST>tmp;
    switch (this->lexer->next_token())
    {
    case TOK::ASSIGN:
    case TOK::ADDASSIGN:
    case TOK::SUBASSIGN:
    case TOK::MULASSIGN:
    case TOK::DIVASSIGN:
    case TOK::MODASSIGN:
    case TOK::ANDASSIGN:
    case TOK::XORASSIGN:
    case TOK::ORASSIGN:
    case TOK::SALASSIGN:
    case TOK::SARASSIGN:
        tmp.reset(new AssignAST);
        switch (this->lexer->next_token())
        {
        case TOK::ASSIGN:   tmp->op = AssignAST::ASSIGN; break;
        case TOK::ADDASSIGN:tmp->op = AssignAST::ADD; break;
        case TOK::SUBASSIGN:tmp->op = AssignAST::SUB; break;
        case TOK::MULASSIGN:tmp->op = AssignAST::MUL; break;
        case TOK::DIVASSIGN:tmp->op = AssignAST::DIV; break;
        case TOK::MODASSIGN:tmp->op = AssignAST::MOD; break;
        case TOK::ANDASSIGN:tmp->op = AssignAST::AND; break;
        case TOK::XORASSIGN:tmp->op = AssignAST::XOR; break;
        case TOK::ORASSIGN:tmp->op = AssignAST::OR; break;
        case TOK::SALASSIGN:tmp->op = AssignAST::SAL; break;
        case TOK::SARASSIGN:tmp->op = AssignAST::SAR; break;
        default:break;
        }
        this->lexer->match(this->lexer->next_token());
        if (!ret->is_lval())
        {
            std::cerr << "The assign operator must have an l-value as its left operand." << std::endl;
            exit(-1);
        }
        tmp->content = std::move(ret);
        tmp->expr = this->parseAssignExp();
        return tmp;
    default:
        return ret;
    }
}
void AssignAST::Dump() const
{
    cout << "{\"type\":\"Assign\",\"op\":\"";
    switch (this->op)
    {
    case AssignAST::ASSIGN:cout << "="; break;
    case AssignAST::ADD:cout << "+="; break;
    case AssignAST::SUB:cout << "-="; break;
    case AssignAST::MUL:cout << "*="; break;
    case AssignAST::DIV:cout << "/="; break;
    case AssignAST::MOD:cout << "%="; break;
    case AssignAST::AND:cout << "&="; break;
    case AssignAST::OR:cout << "|="; break;
    case AssignAST::XOR:cout << "^="; break;
    case AssignAST::SAL:cout << "<<="; break;
    case AssignAST::SAR:cout << ">>="; break;

    default:
        break;
    }
    cout<<"\",\"lVal\":";
    this->content->Dump();
    cout << ",\"expr\":";
    this->expr->Dump();
    cout << "}";
}

bool AssignAST::is_lval() const
{
    return false;
}

void AssignAST::Analyze() const
{
    this->expr->Analyze();
    if (this->op != AssignAST::ASSIGN)
        if (this->op == AssignAST::MUL || this->op == AssignAST::DIV || this->op == AssignAST::MOD){
            GENASM(cmd_code::MOV, this->content->get_address(), CVR(ECX));
        }
        else
            GENASM(cmd_code::MOV, this->content->get_address(), CVR(EDX));
    switch (this->op)
    {
    case AssignAST::ADD:GENASM(cmd_code::ADD, CVR(EDX), CVR(EAX)); break;
    case AssignAST::SUB:GENASM(cmd_code::SUB, CVR(EDX), CVR(EAX)); break;
    case AssignAST::MUL:GENASM(cmd_code::IMUL, CVR(ECX), CVR(EAX)); break;
    case AssignAST::DIV:
        GENASM(cmd_code::CLTD, cmd_val(), cmd_val());
        GENASM(cmd_code::IDIV, CVR(ECX), cmd_val()); 
        break;
    case AssignAST::MOD:
        GENASM(cmd_code::CLTD, cmd_val(), cmd_val());
        GENASM(cmd_code::IDIV, CVR(ECX), cmd_val());
        GENASM(cmd_code::MOV, CVR(EDX), CVR(EAX));
        break;
    case AssignAST::AND:GENASM(cmd_code::AND, CVR(EDX), CVR(EAX)); break;
    case AssignAST::OR:GENASM(cmd_code::OR, CVR(EDX), CVR(EAX)); break;
    case AssignAST::XOR:GENASM(cmd_code::XOR, CVR(EDX), CVR(EAX)); break;
    case AssignAST::SAL:GENASM(cmd_code::SAL, CVR(EDX), CVR(EAX)); break;
    case AssignAST::SAR:GENASM(cmd_code::SAR, CVR(EDX), CVR(EAX)); break;
    }
    cmd_code code1;
    code1.op = cmd_code::MOV;
    code1.type1 = CVR(EAX);
    code1.type2 = this->content->get_address();
    CurrentFuncMgr->code_list.push_back(code1);
}
